#ifndef INI_FILE_PARSER_H
#define INI_FILE_PARSER_H

#include "IniFileLexer.h"
#include "IniFileHandler.h"

namespace IniFile
{

class Parser
{
    Lexer *lexer_;
    Handler *handler_;
    int lookahead_;
private:
    void match(const int token)
    {
        if (token == lookahead_) {
            lookahead_ = lexer_->getToken();
        } else
            handler_->onError(lexer_->getRow(), lexer_->getColumn(),
                              ERROR_SYNTAX_WRONG_TOKEN, token);
    }

    void discard_line()
    {
        while (lookahead_ != TOKEN_LINE_TERMINATOR
                && lookahead_ != TOKEN_EOF)
            match(lookahead_);
    }

    void section_declaration()
    {
        match('[');
        if (lookahead_ == TOKEN_STRING
                || lookahead_ == TOKEN_NON_QUOTED_STRING)
        {
            std::string name = lexer_->getLexeme();
            match(lookahead_);
            handler_->onSection(name);
        }
        else
            handler_->onError(lexer_->getRow(), lexer_->getColumn(),
                              ERROR_SYNTAX_NAME_EXPECTED, lookahead_);
        match(']');
    }

    void property_assignment()
    {
        std::string name;
        if (lookahead_ == TOKEN_STRING
                || lookahead_ == TOKEN_NON_QUOTED_STRING)
        {
            name = lexer_->getLexeme();
            match(lookahead_);
        }
        else
        {
            handler_->onError(lexer_->getRow(), lexer_->getColumn(),
                              ERROR_SYNTAX_NAME_EXPECTED, lookahead_);
            discard_line();
            return;
        }
        if (lookahead_ == TOKEN_SEPARATOR)
            match(TOKEN_SEPARATOR);
        else
        {
            handler_->onError(lexer_->getRow(), lexer_->getColumn(),
                              ERROR_SYNTAX_SEPARATOR_EXPECTED, lookahead_);
            discard_line();
            return;
        }
        std::string value;
        if (lookahead_ == TOKEN_STRING
                || lookahead_ == TOKEN_NON_QUOTED_STRING)
        {
            value = lexer_->getLexeme();
            match(lookahead_);
        }
        else
        {
            handler_->onError(lexer_->getRow(), lexer_->getColumn(),
                              ERROR_SYNTAX_VALUE_EXPECTED, lookahead_);
            discard_line();
            return;
        }
        handler_->onProperty(name, value);
    }

    void statement()
    {
        switch (lookahead_)
        {
        case '[':
            section_declaration();
            break;
        case TOKEN_NON_QUOTED_STRING:
        case TOKEN_STRING:
            property_assignment();
            break;
		case TOKEN_COMMENT_STARTER:
            discard_line();
        case TOKEN_LINE_TERMINATOR:
			break;
        default:
            handler_->onError(lexer_->getRow(), lexer_->getColumn(),
                              ERROR_SYNTAX_STATEMENT_EXPECTED, lookahead_);
            discard_line();
        }
    }

    void statement_terminator()
    {
        switch (lookahead_)
        {
        case TOKEN_EOF:
            break;
        case TOKEN_LINE_TERMINATOR:
            match(TOKEN_LINE_TERMINATOR);
            break;
        case TOKEN_COMMENT_STARTER:
            discard_line();
            if (lookahead_ == TOKEN_LINE_TERMINATOR)
                match(TOKEN_LINE_TERMINATOR);
            break;
        default:
            handler_->onError(lexer_->getRow(), lexer_->getColumn(),
                              ERROR_SYNTAX_STATEMENT_TERMINATOR_EXPECTED, lookahead_);
        }
    }
public:
    Parser(Lexer *lexer, Handler *handler) :
        lexer_(lexer), handler_(handler), lookahead_(TOKEN_UNKNOWN)
    {
    }
	void parse()
	{
	    lookahead_ = lexer_->getToken();
		do {
			statement();
			statement_terminator();
		} while (lookahead_ != TOKEN_EOF);
	}
};

} // namespace IniFile

#endif
